/*
 *  libc3d.c
 *  c3d
 *
 *  Created by Joel Brauer on 7/6/09.
 *
 */

#include "libc3d.h"

//init variables and open c3d file
int libc3d_init(char* filename)
{
	libc3d_header =(libc3d_c3dHeader *) malloc(sizeof(libc3d_c3dHeader));
	//	printf("the c3dHeader structure has a size of %d \n",sizeof(*libc3d_header));
	libc3d_fp = fopen(filename,"rb");
	
	if( libc3d_fp == NULL )
	{
		printf ( "libc3d: ERROR: cannot open file: %s\n\n", filename ) ;
		exit(404); //false
	}
	
	fread(libc3d_header,1, sizeof(libc3d_c3dHeader), libc3d_fp);
	libc3d_loadParameters();
	libc3d_read_labels();	
	libc3d_dataf = libc3d_header->frstFrame;
	libc3d_datarow = (libc3d_data *) malloc(sizeof(libc3d_data)*libc3d_PointCnt);

	return 1;
}

libc3d_read_labels(){
	libc3d_paramInfo *parm1 = (libc3d_paramInfo*) malloc(sizeof(libc3d_paramInfo));

	parm1->fullname = "POINT:LABELS";
	if(!libc3d_getParameter(parm1))
	{
		printf("Parameter Not found %s\n", parm1->fullname);
		return 0;
	}
	char *data = parm1->data;
	libc3d_pointLabels =(char **) malloc(parm1->dimSizes[0]);
	int i,j;
	libc3d_PointCnt = parm1->dimSizes[0];
	for(i=0; i<parm1->dimSizes[0]; i++)
	{	
		libc3d_pointLabels[i] =(char*) malloc(parm1->dimSizes[1]+1);
		for(j=0; j<parm1->dimSizes[1]; j++)
		{
			memcpy(&libc3d_pointLabels[i][j], data, 1);
			data ++;
		}
	}
	for(i=0; i<parm1->dimSizes[0]; i++)
	{
		libc3d_pointLabels[i][parm1->dimSizes[1]] = '\0';
	}
	
	libc3d_paramInfo *parm2 = (libc3d_paramInfo*) malloc(sizeof(libc3d_paramInfo));
	
	parm2->fullname = "ANALOG:LABELS";
	if(!libc3d_getParameter(parm2))
	{
		printf("Parameter Not found %s\n", parm2->fullname);
		return 1;
	}
	data = parm2->data;
	libc3d_analogLabels = (char**) malloc(parm2->dimSizes[0]);
	libc3d_AnalogCnt = parm2->dimSizes[0];
	for(i=0; i<parm2->dimSizes[0]; i++)
	{	
		libc3d_analogLabels[i] = (char*) malloc(parm2->dimSizes[1]+1);
		for(j=0; j<parm2->dimSizes[1]; j++)
		{
			memcpy(&libc3d_analogLabels[i][j], data, 1);
			data ++;
		}
	}
	for(i=0; i<parm2->dimSizes[0]; i++)
	{
		libc3d_analogLabels[i][parm2->dimSizes[1]] = '\0';
	}
	libc3d_freeParmInfo(parm1);
	libc3d_freeParmInfo(parm2);
}

int libc3d_freeParmInfo(libc3d_paramInfo *parm){
    // had to remove most of the elements I was freeing due to SIGALRT's
    // this function should at least reduce memory leak.
    free(parm->data);
    //free(parm->desc);
    free(parm);
}

int libc3d_close(){
//char** libc3d_pointLabels, libc3d_analogLabels;
	int i;
        if(libc3d_PointCnt>0){
            for(i=0; i<sizeof(libc3d_pointLabels); i++){
                    free(libc3d_pointLabels[i]);
            }
        }
        if(libc3d_AnalogCnt>0){
            for(i=0; i<sizeof(libc3d_analogLabels); i++){
                    free(libc3d_analogLabels[i]);
            }
        }
	free(libc3d_datarow);
	free(libc3d_analogLabels);
	free(libc3d_header);
	fclose(libc3d_fp);
	return 0;
}

int libc3d_loadParameters()
{
	int parmLoc = (libc3d_header->parmIdx-1)*0x200+2;
	fseek(libc3d_fp, parmLoc, SEEK_SET);
	char parmSize;
	fread(&parmSize, 1, 1, libc3d_fp);
	//printf("parameter size is %d\n",parmSize);
	libc3d_parameters = malloc(parmSize * 0x200);
	fread(&libc3d_procType, 1, 1, libc3d_fp);
	libc3d_procType -=83;
	//printf("Processor Type is %d\n", procType);
	fread(libc3d_parameters, 1, (parmSize * 0x200) - 4, libc3d_fp);
	return 0;
}

int libc3d_getParameter (libc3d_paramInfo * parm)
{
	parm->name = strchr(parm->fullname, ':');
	if(parm->name == NULL)
	{
		return 0;
	}
	parm->group = malloc(parm->name-parm->fullname+1);
	strncpy(parm->group, parm->fullname, parm->name-parm->fullname);
	parm->group[parm->name-parm->fullname+1] = '\0';
	parm->name++;
	// find the group
	char groupNameSize = strlen(parm->group);
	char groupID;
	char grpNameSize; //temp size holder for use as we're stepping through each entry;
	int found = 0; //set found to false;
	char * sp; //search pointer
	char * tmpName = malloc(groupNameSize); //holder 
	short jumpLength; //distance to next record;
	sp = libc3d_parameters;
	while(!found)
	{
		memcpy(&grpNameSize, sp, 1);
		sp++;
		if(grpNameSize < 0) //locked
			grpNameSize *= -1;
		if(grpNameSize == groupNameSize)
		{
			memcpy(&groupID, sp, 1);
			sp++;
			if(groupID<0)////is it a group? (<0 yes >0 no)
			{
				memcpy(tmpName, sp, groupNameSize);
				if(strcmp(tmpName, parm->group)==0)
				{
					found =1;
					break;
				}
			}
		}
		else 
		{
			sp++;
		}
		
		sp +=grpNameSize;
		memcpy(&jumpLength, sp, 2);
		if(jumpLength == 0) break; //done looking;
		sp +=jumpLength; //jump to next record...
		
	}
	if(!found)//group not found
	{
		return 0;
	}
	
	int parmNameSize = strlen(parm->name);
	sp = libc3d_parameters; //reset pointer;
	found = 0;
	char tmpID;
	free(tmpName);
	tmpName = malloc(parmNameSize);
	groupID*=-1; //since group ID's are negative and we are now looking for the positive counterpart
	while(!found)
	{
		memcpy(&grpNameSize, sp, 1);
		sp++;
		if(grpNameSize < 0) //locked
			grpNameSize *= -1;
		memcpy(&tmpID, sp, 1);
		sp++;
		if(tmpID == groupID && grpNameSize == parmNameSize)
		{
			memcpy(tmpName, sp, parmNameSize);
			if(strcmp(tmpName, parm->name)==0)
			{
				found =1;
				break;
			}
		}
		sp +=grpNameSize;
		memcpy(&jumpLength, sp, 2);
		if(jumpLength == 0) break; //done looking;
		sp +=jumpLength; //jump to next record...
	}
	free(tmpName);
	if(!found)
	{	
		return 0;
	}
	sp += grpNameSize+2;
	parm->type = libc3d_getParmType(sp);
	sp++;
	parm->dim = libc3d_getParmDimCnt(sp);
	sp++;
	parm->dataSize = 1;
	if(parm->dim > 1)
	{
		parm->dimSizes = malloc(sizeof(char)*parm->dim);
		memcpy(parm->dimSizes, sp, parm->dim);
		libc3d_dimensionSwap(parm); //changes from FORTRAN to C format
		int i;
		for(i=0; i<parm->dim; i++)
		{
			parm->dataSize *= parm->dimSizes[i];
		}		
	}
	sp += parm->dim;
	switch(parm->type)
	{
		case -1: //character data
			//printf("char");
			parm->data =(char*) malloc(sizeof(char)*parm->dataSize);
			memcpy(parm->data, sp, 1*parm->dataSize);
			break;
		case 1: //single byte data
			//printf("byte");
			parm->data =(char*) malloc(sizeof(char)*parm->dataSize);
			memcpy(parm->data, sp, 1*parm->dataSize);
			break;
		case 2: //integer data(16-bit)
			//printf("short");
			parm->data =(short*) malloc(sizeof(short)*parm->dataSize);
			memcpy(parm->data, sp, 2*parm->dataSize);
			break;
		case 4: //float data
			//printf("float");
			parm->data =(float*) malloc(sizeof(float)*parm->dataSize);
			memcpy(parm->data, sp, 4*parm->dataSize);
			break;
		default:
			printf("error invalid parameter type %d\n", parm->type);
			return 0;
	}
	return strlen(parm->name);
}

char libc3d_getParmType(void *sp)
{
	char typeInd;
	memcpy(&typeInd, sp, 1);
	return typeInd;
}

char libc3d_getParmDimCnt(void *sp)
{
	char dimCount;
	memcpy(&dimCount, sp, 1);
	return dimCount;
}

void libc3d_dimensionSwap(libc3d_paramInfo *parm)
{
	char* tmpdims = (char*) malloc(sizeof(char)*parm->dim);
	int i;
	for( i=0; i<parm->dim; i++)
	{
		tmpdims[i] = parm->dimSizes[parm->dim-i-1];
	}
	free(parm->dimSizes);
	parm->dimSizes = tmpdims;
	return;
}

int libc3d_readData()
{
	//TODO: At the moment since I know the data I'm reading is float, I'm doing a LOT of assumtion in this code
	//TODO: I need to come back and fix it to work with both INT and FLOAT data, as well as ANALOG.
	short i;
	
	//printf("SCALE:FACTOR is %f : Start Frame is %d : Last Frame is %d\n", header->scaleFactor, header->frstFrame, header->lastFrame);
	if(libc3d_readSize == 0)//initialize if first read
	{
		if(libc3d_header->scaleFactor<0) //float data
		{
			libc3d_readSize = 4; //4 bytes for reading float values
			/*libc3d_x = malloc(libc3d_readSize);
			libc3d_y = malloc(libc3d_readSize);
			libc3d_z = malloc(libc3d_readSize);
			libc3d_extra = malloc(libc3d_readSize);*/
		}
		else
		{
			libc3d_readSize = 2; //2 bytes for short int values
			/*libc3d_x = malloc(libc3d_readSize);
			libc3d_y = malloc(libc3d_readSize);
			libc3d_z = malloc(libc3d_readSize);
			libc3d_extra = malloc(libc3d_readSize);*/
		}
		// move file pointer to the start of the Data
		libc3d_startLoc = (libc3d_header->dataStart-1)*0x200;
		
		fseek(libc3d_fp, libc3d_startLoc, SEEK_SET); //dataStart is a 1 based inde
	}

	if(libc3d_dataf == libc3d_header->lastFrame) // return false if reached end of data
	{
		libc3d_EOF = 1; //set end of file indicator
		return 0;
	}
	for(i=0; i<libc3d_PointCnt; i++)
	{
		fread(&libc3d_datarow[i].x, libc3d_readSize, 1, libc3d_fp); 
		fread(&libc3d_datarow[i].y, libc3d_readSize, 1, libc3d_fp);
		fread(&libc3d_datarow[i].z, libc3d_readSize, 1, libc3d_fp);
		fread(&libc3d_datarow[i].flags, libc3d_readSize, 1, libc3d_fp);
		// old debug code printf(", \"x=%G,y=%G,z=%G\"", *libc3d_x, *libc3d_y, *libc3d_z);
	}
	libc3d_dataf++;
	
	return 1;//return true
}
